#pragma once

#include <fstream>
#include <string>
#include <map>
#include <vector>

#include <vbl/img/ImageDefs.h>
#include <vbl/img/ImageAdaptorImage.h>
#include <vbl/Exception.h>
#include <vbl/util/Array2D.h>
#include <vbl/util/StringUtils.h>
#include <vbl/util/FileUtils.h>
#include <vbl/calib/BogUtils.h>
#include <vbl/img/proc/ImageUndistort.h>
#include <vbl/img/stat/Histogram.h>
#include <vbl/img/ImageCopy.h>
#include <vbl/img/io/ImageIO.h>
#include <vbl/img/proc/Demosaic.h>
#include <vbl/img/ImageTransform.h>
#include <vbl/img/ImageAdaptorCV.h>
#include <vbl/util/window.h>
#include <vbl/img/conv/LAB.h>

#include <boost/regex.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/filesystem.hpp>

using namespace std;
using namespace boost;
using namespace vbl;

#define USE_CAS_DATASET
#define USE_OLD_DAT_NOTATION
#define USE_OLD_FILESYSTEM

typedef img::Image<img::Triple<double>> ImageLab;

typedef pair<vector<util::Array2D<double>>,vector<util::Array2D<double>>> dist_map_t;
typedef vbl::img::stat::Histogram< 3, ImageLab::value_type, 20, double > AppearanceHist;



struct rectangle 
{
	bool isVisible;
	int xmin;
	int xmax;
	int ymin;
	int ymax;
};

class ProgramOptions
{
private:
	/// Calculates the overlap between two rectangles
	/// @param[in]	curr
	/// @param[in]	comp
	/// @return		The overlap
	int calcOverlap( rectangle curr, rectangle comp );

	/// Computes the camera parameters from its .bog files
	void computeCameraParameters();

	/// Creates and computes a map containing for every location all locations occluding it for a least 10%
	void computeOcclusionMap();

	/// Compute undistortion mapping for a specific camera
	/// @param[in]	bogData
	/// @param[out]	mapXMat
	/// @param[out]	mapYMat 
	void computeUndistortMap( const calib::BogData& bog_data, util::Array2D<double> & map_x_mat, 
		util::Array2D<double> & map_y_mat );

	/// Retrieves the raw image file paths per camera for all frames using the MCG_dataset header.
	/// @param[out]	files :				per camera vector containing vector of strings containing the file paths
	void getFilesPerCamera( vector<vector<string>> & files );

	/// Retrieves the raw image file paths per camera for all frames using a regular expression.
	/// @param[out]	files :				per camera vector containing vector of strings containing the file paths
	void getFilesPerCamera_old( vector<vector<string>> & files );

	/// Retrieves the raw image file paths per camera for all frames
	/// @param[out]	files :				per camera vector containing vector of strings containing the file paths
	void getImagePaths( vector<vector<string>> & filesPerCamera );

	/// Parses the line of a POM file, only searches for rectangle information.
	/// @param[in]	line :				the line to be parsed
	void parseLine( string line );

	/// read camera calibration and compute camera matrices
	/// @param[in]	bog_path
	/// @param[out]	bog_data
	void readBogData( const string & bog_path, calib::BogData * bog_data );

	/// Reads a config file and sets its contents to their respective variables
	/// @param[in]	config_file_path :	path to the config file
	/// @param[out]	scenario_name :		name of the scenario currently being run
	/// @param[out]	proba_path :		path to the pom files of this scenario
	/// @param[out]	image_path :		path to the images to be used, either raw of png. 
	///										(Set use_png to define which should be used)
	/// @param[out] background_path :	path to background images after they have been through POM
	/// @param[out] pom_file :			path to the POM file used to generate the proba files and background images
	/// @param[out] output_path :		path to where all output should be stored. 
	///										A new folder with the scenario name will be created.
	/// @param[out] bog_path :			path to the bog files used for camera calibration
	/// @param[out] pom_x :				the number of locations on the x-axis on the POM grid
	/// @param[out] pom_y :				the number of locations on the y-axis on the POM grid
	/// @param[out] begin_frame :		the begin frame of the scenario
	/// @param[out] num_of_frames :		the number of frames to proces
	/// @param[out] batch_size :		the batch size to be used
	/// @param[out] pass_prob :			the average probability a path should have to be accepted
	void readConfigFile(string config_file_path, string & scenario_name, string & proba_path, string & image_path, 
					string & background_path, string & pom_file, string & output_path, string & bog_path, unsigned int & pom_x, 
					int & pom_y, int & begin_frame, unsigned int & num_of_frames, int & batch_size, double & pass_prob,
					vector<int> & sources_GT, vector<pair<vector<int>,vector<int>>> & template_locs );

	/// Reads the POM file (defined earlier) and saves all the rectangles defined in it
	void readPomFile();

	/// Reads the proba files containing the POM information
	/// @param[in]	frame :				the frame number to be processed
	/// @param[out]	proba :				vector of POM probabilities
	void readProbaFile( int frame, vector<double> & proba );
	
	//template <typename T, template <class> class AP1, template <class> class AP2>
	void undistortImage( const img::ImageRGB & capuredImage, img::ImageRGB & undistortedImage, 
									const util::Array2D<double> & mapXMat, const util::Array2D<double> & mapYMat );

	void getAppearance2( int frame, int location, vector<ImageLab> & ims, vector<AppearanceHist> & appearance,
						  vector<bool> & visibility, vector<double> proba );

	bool isOccluded( int frame, int location, int cam, vector<double> proba );

	void getImages( int frame, vector<img::ImageRGB> & ims_out );

	void getAppearance( rectangle r, ImageLab & im, img::ImageRGB & background, AppearanceHist & apHist );

	void setupTemplates( vector<pair<vector<int>,vector<int>>> & template_locs );

	void getImage( int cam, int abs_frame, img::ImageRGB & im );

	void getImageLab( int cam, int abs_frame, ImageLab & im_out );

	void getImageBG( int cam, int abs_frame, img::ImageRGB & im );

	void getTemplateOneCam( int cam, int rel_frame, int loc, AppearanceHist & ap_hist );

	void getImagesLab( int frame, vector<ImageLab> & ims_out );

public:
	string scenario_name, proba_path, image_path, background_path, pom_file, output_path, bog_path;
	int pom_y, begin_frame, batch_size, num_of_batches, max_detections, vsource, num_of_cams,
		total_locations, image_width, image_height;
	unsigned int num_of_frames, pom_size, pom_x;
	double pass_prob;
	vector<double> rot;
	vector<int> sources_GT;
	bool b_save_images, use_appearance, use_appearance_shitrit, first_graph, use_png, use_alt_sources;
	vector<vector<rectangle>> pom_locations; // pom_locations[ cam ][ location ]
	vector<vector<string>> files_per_camera; // files_per_camera[ cam ][ file string ]
	dist_map_t dist_map;
	vector<vector<AppearanceHist>> templates; // templates[ template ][ cam ]
	vector<vector<bool>> camera_visibility; // camera_visibility[ template ][ cam ]
	vector<map<int,vector<int>>> occlusion_map; // occlusion_map[ cam ][ location ]
	vector<vector<float>> probabilities; // probabilites[ timestep ][ location ]

	ProgramOptions( string config_file_path );

	//template <typename T, template <class> class AP1>
	void undistortImage( img::ImageRGB & capuredImage, const util::Array2D< double >& mapXMat, 
									const util::Array2D< double >& mapYMat );

	int calculateSurface( rectangle r );
};